Was macht eigentlich ein Bundler? | IT-Neuigkeiten

2021-11-26 03:28:34 By : Ms. Su li

Unser Newsletter informiert Sie regelmäßig und kostenlos über Neuigkeiten, Artikel und Veranstaltungen zu aktuellen IT-Themen.

In der Frontend-Entwicklung kommt man selten ohne JavaScript aus. Erfahrene Backend-Profis werden dann mit einer völlig neuen Toolchain konfrontiert, die vor unbekannten Fachbegriffen strotzt. Doch hinter der Komplexität steckt ein System.

Eine Serverkomponente liefert über HTTP einen Strauß von HTML-, JavaScript-, CSS- und anderen Dateien (zB Bilder und Schriftarten) an einen Browser. Der Browser fügt die Teile zusammen und rendert eine komplette Website. Unabhängig davon, welcher Technologie-Stack tatsächlich zum Einsatz kommt, soll dieser Vorgang vordergründig möglichst schnell sein, auf allen Browsern „gut“ aussehen und natürlich eine gute Benutzeroberfläche bieten.

Als Beispiel für diesen Artikel dient eine Anwendung, die React als clientseitiges JavaScript-Framework verwendet. Denn gerade hier sind Sie auf eine ganze Reihe von Tools angewiesen, die aus dem JavaScript-Quellcode für Browser leicht verdauliche Snacks erstellen. Mit diesem Beispiel können wir uns also einen guten Überblick über die gängigen Technologien verschaffen. Grundsätzlich lassen sich die hier erläuterten Konzepte auch für klassische auf dem Server gerenderte Anwendungen anwenden.

Das Hauptproblem, das alle Webanwendungen (und sogar einfache Websites) plagt, ist die Netzwerkverbindung. Diese kann in der Regel jederzeit abbrechen, langsam und unzuverlässig sein. Weit entfernt ist jeder Nutzer über Glasfaser angebunden.

Doch auch bei einer perfekten Verbindung gibt es natürliche Grenzen: Browser müssen sich entscheiden, ob sie die für eine Website benötigten Dateien parallel oder sequentiell anfordern sollen. Parallele Verbindungen haben den Vorteil, dass Downloads schneller sind, sie haben aber auch einen Overhead pro Verbindungsaufbau.

Moderne Browser nutzen clevere Heuristiken oder neuere Protokolle wie HTTP/2 oder HTTP/3 (auch bekannt als „QUIC“), um das letzte Quäntchen Performance aus der Verbindung herauszuholen. Als Entwickler haben Sie: aber selten Einfluss.

Der weitaus größere Hebel besteht darin, die Anzahl der erforderlichen Anfragen von Anfang an zu reduzieren. Es gibt zwei Techniken, die miteinander kombiniert werden können:

Frontend-Tools können dies für Entwickler tun: daher der Name "Bündel"; ein Build-Schritt, der mit der statischen Verknüpfung vergleichbar ist. Für heutige Tools geht dies jedoch nicht weit genug, da sie neben der Kombination von JavaScript-Dateien (siehe folgenden Abschnitt) auch andere Arten von Ressourcen, wie bspw. Bilder und Stile, verarbeiten. Aus diesem Grund findet man manchmal die Begriffe "Front-End-Build-Tool" und "Asset-Pipeline". Aber dazu später mehr.

Was können Sie sich jetzt unter "Bündelung" vorstellen? Bestimmte Ressourcen, die in mehreren Einzeldateien vorhanden sind, können problemlos kombiniert werden. Als Beispiel: Mehrere CSS-Dateien, die über <link> in den HTML-Code eingebunden werden, können durch einfaches Aneinanderreihen zu einer einzigen Datei zusammengefasst werden. Dies führt zu nur einer von mehreren notwendigen GET-Anfragen an den Server.

Dies ist auch mit JavaScript möglich, sogar erforderlich. Denn Bibliotheken wie React bestehen intern aus zahlreichen Einzeldateien; so wie man es beispielsweise von typischen Backend-Programmiersprachen wie Java gewohnt ist. Moderne Browser können zwar dank des JavaScript-Modul-Standards auch importierte Verweise auflösen (dazu später mehr), dies führt jedoch zum Problem der „Cascading Requests“:

... und so weiter. Jede Zeile generiert ihre eigene HTTP-Anfrage. Der Browser kann nicht mit der Ausführung des JavaScript-Codes beginnen, bis alle (transitiven) Abhängigkeiten geladen wurden.

Die Bündelung beginnt an diesem Punkt und untersucht alle Importe zur Build-Zeit. Anstatt mehrere JavaScript-Dateien auszuliefern, werden diese als Kombination aus Framework- und Anwendungscode verkettet verarbeitet.

Ein weiterer Haken bei der Einbindung von JavaScript-Dateien besteht darin, dass diese vom Browser genau dort blockierend ausgeführt werden, wo das <script>-Tag steht. Wenn diese also im <head> der HTML-Datei erscheinen, pausiert der Browser das Rendern und lädt zuerst den anderen Code herunter. Währenddessen bleibt der Inhalt des Browsers leer. Der Vollständigkeit halber sei gesagt, dass die Verarbeitung der Script-Tags mit dem defer-Attribut kurz vor dem Ende der Datei verschoben werden kann. Dateien, die mit type = "module" geladen werden, werden automatisch mit einer Verzögerung geladen; in diesen ist das Attribut defer redundant.

Je nach Baustil gibt es hier mehrere Möglichkeiten. In Single-Page-Anwendungen ist der JavaScript-Code in erster Linie für das Rendern von Inhalten verantwortlich. Sie müssen also warten, bis es – inklusive des Frameworks – im Browser landet. Wenn Sie sich stattdessen für serverseitiges Rendering entscheiden, können Sie z. B. Verschieben Sie das <script>-Tag an das Ende des HTML-Codes (dh vor </body>).

Darüber hinaus übersetzen Bundler durch die Analyse von Importpfaden auch die Node-Welt in die Browser-Welt. Denn per npm installierte Pakete landen per Konvention im Ordner node_modules. Importiert man dann React via import React aus "react", wird die Datei node_modules/react/index.js unter die Haube geladen – vereinfacht gesagt – die Datei node_modules/react/index.js. Dieser Mechanismus kann in jedem npm-Paket über package.json individuell konfiguriert werden. Aber das ist dem Browser fremd; es würde versuchen, eine GET-Anforderung an die Reaktionsressource zu stellen, die dann mit 404 quittiert würde.

Ein anderer Lösungsansatz hierfür sind sogenannte „Import-Maps“, mit denen ein URL-Remapping für importierte Module definiert werden kann. Ihre Browserunterstützung ist derzeit noch schlecht; außerdem lösen sie nicht alle Bundler-Anwendungsfälle.

Oftmals greift ein Benutzer nicht nur einmal auf eine Website zu. Insbesondere beim serverseitigen Rendering liefert der Webserver mehrere HTML-Seiten aus, wobei es zu einer starken Überschneidung der Ressourcen kommt: Meist verwenden alle Unterseiten das gleiche Stylesheet.

Um Ladezeiten durch wiederholte Aufrufe zu verkürzen, muss man sich also Gedanken über das Caching machen. Alleine dazu könnte man einen ganzen Artikel schreiben, daher kann ich hier nur eine kurze Einführung in den Browser-Cache geben.

Ein Webserver kann in der Antwort auf eine GET-Anfrage das gewünschte Caching-Verhalten angeben. Die meisten Webserver tun dies automatisch. Wenn der Browser eine Ressource anfordert, antwortet der Server nicht nur mit seinem Inhalt, sondern liefert auch bestimmte Metadaten wie das Änderungsdatum und den Hash:

Der Inhalt inklusive Metadaten landet dann im Browser-Cache. Wenn der Benutzer die Ressource erneut anfordert, z. B. B. Beim Folgen eines Links sendet der Browser spezielle Header an den Server:

Damit zeigt der Browser dem Server an, dass er den Inhalt mit diesem Hash bereits hat. Wenn sich die Ressource auf dem Server nicht geändert hat, antwortet sie mit dem Statuscode 304 Not Modified. Dies wiederum signalisiert dem Browser, dass er den Inhalt aus dem Cache laden kann. Der Server muss den Inhalt der Ressource nicht senden und beendet die Antwort mit einem leeren Body.

Richtiges Caching hilft daher, das Datenvolumen klein zu halten. Aber die Bündelung torpediert dieses Verhalten teilweise. Bleiben wir bei React: Neben dem Framework-Code (ca. 132 Kilobyte in Version 17.0.2) gibt es auch den Anwendungscode. Beide landen in derselben Datei, die daher vom Server als eine einzige Ressource behandelt wird.

Leider sorgen sowohl Codeänderungen als auch Framework-Updates dafür, dass sich das Bundle ändert. Im schlimmsten Fall muss der Browser bei jedem Bugfix 132 Kilobyte unveränderten React-Code herunterladen, weil der Webserver nicht weiß, was sich genau geändert hat, nur dass sich irgendwo etwas geändert hat. Was sich zunächst nicht nach viel anhört, summiert sich schnell, wenn neben React noch andere Libraries ins Spiel kommen.

Das Gegenmittel ist das "Code-Splitting". Anstatt alle beteiligten Quelldateien inklusive Bibliotheken unverblümt in einer einzigen Datei zu verschmelzen, ist der Ansatz intelligenter: Normalerweise landen externe Abhängigkeiten in einer Datei und anwendungsspezifischer Code in einer anderen Datei. Ein Bundler stellt sicher, dass die Importpfade korrekt implementiert werden, sodass der Entwicklungsworkflow nicht geändert werden muss. Lediglich Konfigurationsdateien müssen angepasst werden.

Teilweise kann die Codeaufteilung auch manuell erfolgen, da die Konfiguration stellenweise kompliziert ist. Viele Bibliotheken stellen bereits "vorgebündelte" und komprimierte Dateien zum Download bereit, zum Beispiel response.production.min.js in React. Anstatt import React from "react" in eigenen Code zu schreiben, reicht es, auf die globale Variable React zuzugreifen. Weil das bereitgestellte Bundle alle Funktionen in dieser globalen Variablen exportiert.

In der Praxis könnte der Header einer HTML-Datei so aussehen:

Allerdings rate ich von diesem Vorgehen ab, wenn Sie andere Bibliotheken einbinden, da es dann leicht unübersichtlich werden kann.

Einige Frontend-Frameworks, darunter React, ermöglichen auch eine weitere Unterteilung des Anwendungscodes. Zu diesem Zweck kennen neuere JavaScript-Versionen sogenannte „dynamische Importe“. Mit dieser in React-Komponenten integrierbaren Technologie kann ein allgemeines Framework von mehreren Websites gemeinsam genutzt werden, wobei einzelne Seiten dann ihren spezifischen Code nachladen können. Dies geschieht automatisch im Hintergrund: Der Browser beginnt bereits mit dem Rendern des Gerüsts, bevor das „Innere“ zur Verfügung steht. Ein Beispiel dafür finden Sie in der React-Dokumentation:

Bundler können diese dynamischen Importe erkennen und den Anwendungscode entsprechend trennen. Dazu müssen Browser zusätzlich den „ESM“-Standard unterstützen (ab Oktober 2021: über 90%). Alternativ können Sie auch Dateien in einem anderen Modulstandard generieren lassen, wovon ich abraten würde, da dies mit mehr Aufwand verbunden ist.

Eine zu feine Unterteilung führt logischerweise dazu, dass pro Seitenaufruf zu viele HTTP-Requests gesendet werden, denn trotz Caching muss der Browser für jede Ressource eine Anfrage an den Webserver schicken.

Idealerweise sollte der Browser diese Anfrage nicht einmal senden, wenn sich die Ressource auf dem Server nicht geändert hat. Aber woher soll der Browser das wissen? Das Stichwort ist „Fingerabdruck“.

Anstatt ein Skript unter der URL /app.js auszuliefern, können Sie einfach den entsprechenden Hash einfügen: /app.js wird zu /app.48de1f9.js. Die Dateinamen müssen nun im HTML-Code mit dem Hash – dem Fingerabdruck – referenziert werden.

Jetzt müssen Sie den Server konfigurieren, um den folgenden Header für diese Dateien zu setzen:

Damit teilt der Server dem Browser mit, dass sich die Datei unter diesem Pfad nie ändern wird. So kann es bedingungslos aus dem Cache geladen werden. Das beschleunigt das wiederholte Laden der Seite erheblich, denn die schnellste Anfrage ist die, die gar nicht stattfindet.

Der oben angegebene HTML-Header muss wie folgt geändert werden:

Ändert sich später der Inhalt einer Datei, ändert sich auch der Hash und damit die Referenz in HTML, damit der Browser erkennt, dass eine neue Ressource angefordert werden muss. Ein Bundler kann die Hashes automatisch anpassen (siehe folgenden Abschnitt).

Typischerweise wird diese Technik nur auf die sogenannten "Assets" angewendet, dh Stile, JavaScript, Bilder, Schriftarten oder ähnliches. Auf Fingerabdrücke würde bei HTML-Seiten verzichtet, ansonsten würden sich auch die URLs mit jedem Update ändern, was natürlich der Idee von stabilen Links widerspricht.

Zusammenfassend kann man sagen: ETag-basiertes Caching für HTML, Fingerabdruck unveränderlich für alles andere. Ein Bundler stellt sicher, dass die Namen der erstellten Dateien nach der Bündelung den richtigen Fingerabdruck enthalten.

Die Tatsache, dass es sehr schwierig ist, mehrere Quelldateien mit einigen Assetklassen wie Bildern und Schriftarten zu kombinieren, ist eine wichtige Leistungstechnik für dieses Fingerprinting. In Verbindung mit Code-Splitting ist es jedoch ein mächtiges Werkzeug.

Es ist übrigens nicht notwendig, das Skript als app.48de1f9.js im Webserver-Verzeichnis zu speichern. Sie können den Webserver auch so konfigurieren, dass die URL umgeschrieben wird. Aber Vorsicht: Auch Ressourcen mit veralteten Fingerprints sollten mit 404 quittiert werden. Komplette Frameworks wie Rails bieten dies ohne großen Konfigurationsaufwand aus einer Hand.

Leider beeinflusst das Fingerprinting auch den tatsächlichen Inhalt der Ressourcen. Im vorherigen Abschnitt sehen Sie, dass zumindest die <script>-Tags angepasst werden müssen. Aber auch andere Vermögenswerte sind betroffen. Wenn Sie SVG-Dateien für Symbole verwenden, werden deren Pfade in CSS referenziert:

Doch dank Fingerprinting ist die URL plötzlich anders. Die CSS-Deklaration muss daher vom Bundler entsprechend angepasst werden:

Daher ist es wichtig, dass der Bundler so konfiguriert ist, dass er alle Assetklassen verarbeitet. Intern verwenden sie einen Abhängigkeitsbaum, der nicht nur JavaScript-Dateien, sondern auch Stile usw. enthält. React-Anwendungen gehen oft noch einen Schritt weiter und „importieren“ Styles direkt in den JavaScript-Code:

Dies ist jedoch eine Funktion, die in JavaScript tatsächlich nicht vorhanden ist. Der Bundler erweitert gewissermaßen das Sprachangebot, was einerseits praktisch, andererseits aber nicht portabel ist. Da sich die verschiedenen Bundler in der Handhabung eines solchen Imports stark unterscheiden, gehen wir hier nicht weiter darauf ein.

Auf Spracherweiterungen kann man jedoch - zumindest bei React - nicht verzichten. React verwendet den JavaScript-Dialekt "JSX", der die Verwendung von HTML-Tags in JavaScript ermöglicht. Beispiel:

Dieser Code erzeugt zur Laufzeit eine HTML-Liste (<ul>) mit mehreren Einträgen (<li>), die sich aus dem items-Array ergeben. Browser verstehen diese Syntax jedoch nicht. Ich möchte hier nicht über Sinn und Unsinn von JSX philosophieren, sondern erklären, wie daraus eigentlicher JavaScript-Code entsteht. Betreten Sie den "Transpiler" ...

Ein Transpiler oder "Trans-Compiler" ist ein Werkzeug, das verwendet wird, um einen JavaScript-Dialekt in einen anderen zu übersetzen. Das beliebteste seiner Art ist "Babel", dessen Name mit einem Augenzwinkern auf die biblische Sprachverwirrung hinweist. Babel kann nun den obigen JSX-Code kauen und den folgenden JS-Code ausspucken:

Nun ist der Code von eigentümlichen Features befreit und alle Browser - sofern React als Abhängigkeit geladen ist - können damit umgehen. Oder?

Leider muss Babel oft mehr leisten, denn moderner JavaScript-Code verwendet oft zusätzliche Funktionen, die standardisiert, aber längst nicht in allen Browsern verfügbar sind. Internet Explorer 11 ist hier oft auf der Bremse, auf die manche Anwendungen (und Nutzer) noch angewiesen sind. Dies ist ungefähr ein Jahrzehnt zurück, daher wird das Schlüsselwort class nicht einmal unterstützt. Babel kann solche Features ebenfalls entfernen und auf Wunsch solchen prähistorischen JS-Code generieren.

Die technologieunabhängige Datenbank "Browserslist" zeichnet Browserversionen und deren Unterstützung für Sprachversionen auf. Wenn Sie auf diese Weise transpilieren möchten, erstellen Sie eine Konfigurationsdatei, die z. B. sieht so aus:

Babel wertet diese Datei automatisch aus und konfiguriert sich so, dass die beiden aktuellsten Versionen von jedem Browser unterstützt werden, sowie IE 11 und die mit einem deutschlandweiten Marktanteil von mehr als 5 Prozent. Alle Funktionen, die von den angegebenen Browsern nicht unterstützt werden, werden von Babel transpiliert. Achtung: Die Zusammenstellung entspricht nicht immer dem Original; Stellenweise muss Babel allerlei Helferlein einspritzen, um neue Features zu emulieren.

Eine häufige Fehlerquelle bei der Verwendung von Babel ist die oft schlecht verstandene Konfiguration. In vielen Projekten werden experimentelle, noch nicht standardisierte JS-Features frei verwendet (sog. „Proposals“ in Stufe 1, 2 oder 3). Ihre Spezifikation und Implementierung kann sich noch ändern, weshalb ein Update der Babel-Version dazu führen kann, dass der Code nicht mehr funktioniert.

Ganz nebenbei können Transpiler auch mit alternativen Programmiersprachen, wie zB TypeScript, umgehen. Aus ihrer Sicht ist dies nur "noch ein" JavaScript-Dialekt. Auf die sich daraus ergebenden zusätzlichen Herausforderungen wird hier nicht weiter eingegangen.

Wenn Sie denken, dass dies die Unterstützung alter Browser abdeckt, haben Sie die Rechnung leider ohne die Web-APIs gemacht. Nicht nur JavaScript als Sprache entwickelt sich weiter, Browser stellen auch neue APIs in atemberaubender Geschwindigkeit zur Verfügung. Mit den „Custom Elements“ können Sie beispielsweise – wie der Name schon sagt – eigene HTML-Tags definieren, die Ihre Inhalte mit zusätzlichen Funktionen bereichern können.

Für die armen 5 Prozent der User, deren Browser damit nichts anfangen kann (Danke, IE!), kann Babel auch nichts anfangen, denn als reiner Transpiler kennt es nichts von Programmierschnittstellen. Ähnlich ist es z. B. mit fetch, dem wesentlich einfacher zu bedienenden Nachfolger von XMLHttpRequest. Was also tun

Die Lösung heißt „Polyfills“, ein Bündel ganz unterschiedlicher Technologien, die nach einer Füllermarke benannt sind. Für viele moderne APIs gibt es ein Polyfill, das in der Regel ein Ausschnitt aus JavaScript-Code ist, der die API so weit wie möglich mit den grundlegenden Tools des Browsers emuliert. Dies funktioniert nicht immer vollständig, insbesondere wenn das neue Feature tief im Browser verankert ist. Bei fetch setzt der entsprechende Polyfill die Funktionalität auf Basis von XMLHttpRequest um und verpasst ihr einen neuen Anstrich.

Es sollte jedoch beachtet werden, dass jedes Polyfill die Größe der Website aufbläst. Bei der Architektur müssen Sie sich entscheiden, ob Sie lieber den „Progressive Enhancement“-Ansatz verwenden: Sie gestalten die Website so, dass sie ganz ohne JavaScript auskommt. Moderne Browser erhalten dann als Bonus zusätzliche Annehmlichkeiten. Benutzerdefinierte Elemente sind dafür ideal. Im Vorbeigehen bauen Sie Barrieren ab, damit die Webanwendung auch von eingeschränkten Benutzern (zB alten Smartphones oder Benutzern mit Screenreader) genutzt werden kann.

Progressive Enhancement ist jedoch nur bedingt mit Single-Page-Anwendungen kompatibel. Bei diesen sind Sie oft gezwungen, Spachtelmasse großflächig aufzutragen.

Beiden Ansätzen ist jedoch gemeinsam, dass sie – um sie richtig anwenden zu können – einige Vorarbeiten benötigen, um die spezifischen Anforderungen im Projekt auszuloten und die Weichen für die Entwicklung entsprechend zu stellen. Wenn Sie sich für Polyfills entscheiden, können Bundler diese automatisch in die erstellten Bundles injizieren. An manchen Stellen können Sie sogar aus den Quelltexten und den Konfigurationen der Browserliste auf die benötigten Polyfills schließen.

Darüber hinaus ist zu beachten, dass die Transpilation von Sprachmerkmalen, wie sie von Babel praktiziert wird, in der Regel die Semantik erhält, Polyfills jedoch naturgemäß Kompromisse eingehen müssen. Lesen Sie daher unbedingt die Dokumentation sorgfältig durch.

Während Babel hervorragend mit JavaScript umgehen kann, wirkt sich die marode Browserunterstützung oder die Vorverarbeitung auch auf andere Arten von Assets aus. Moderne Browser können beispielsweise mit den Bildformaten WEBP und AVIF umgehen, die Dateien erzeugen, die deutlich kleiner als JPG sind. HTML 5 bietet die Möglichkeit, mehrere verschiedene Bildformate anzugeben und dem Browser die Wahl zu überlassen:

Ein Browser, der damit nichts anfangen kann, lädt wie gewohnt das JPG-Format. Wenn er jedoch AVIF rendern kann, lädt er einfach diese Version. Ab Oktober 2021 können nur rund zwei Drittel der Webnutzer weltweit AVIF-Dateien anzeigen. Diese profitieren dann jedoch von deutlich schnelleren Ladezeiten.

Dieses Problem kann beliebig kompliziert sein, denn um die Website zum Laufen zu bringen, können Sie auch Bilder in verschiedenen Größen für verschiedene Displaygrößen liefern. Da spätestens hier viele Bundler ausfallen, gibt es einige spezialisierte Drittanbieter, die Bilder je nach Wunsch „on the fly“ skalieren, konvertieren und optimieren können – natürlich mit Caching-Unterstützung. Die verwendete Technologie wird als „Content Negotiation“ bezeichnet, wobei der Anbieter die Request-Header des Browsers auswertet, um festzustellen, was der Browser verarbeiten kann.

Aus technischer Sicht hat das Transkodieren und Skalieren von Bildern absolut nichts mit der Verarbeitung von JavaScript zu tun, gehört also eigentlich nicht zur Kernaufgabe von Bundlern. Dennoch können auch viele Asset-Pipelines damit umgehen, weil sie beispielsweise helfen, besonders kleine Ressourcen zu inlinen. Also z. B. ein Bild wie folgt optimiert werden kann: aus

Diese Ersetzung kann sowohl in HTML-, CSS- als auch in JavaScript-Ressourcen erfolgen. Auch hierfür sind ähnliche Überlegungen zur Balance zwischen Cachefähigkeit und kaskadierender Anfragen anzustellen wie beim Code-Splitting.

Bleibt noch eine letzte wichtige Aufgabe für Bündeler: das sogenannte "Baumrütteln"; an anderer Stelle auch als "Dead Code Elimination" bekannt.

Auf der Serverseite spielt es normalerweise keine Rolle, wie groß das ausführbare Artefakt ist. Alle transitiven Abhängigkeiten – auch wenn sie am Ende nicht benötigt werden – sind im Image enthalten. Wie hier schon lang und breit erklärt, ist die Bandbreite auf der Client-Seite wertvoll.

Tree Shaking beschreibt den Vorgang, mit dem der Bundler ungenutzte Quelldateien aus dem erstellten Bundle „herausschüttelt“. Dafür müssen Sie nicht wirklich hart arbeiten, da Sie sowieso einen Abhängigkeitsbaum für das Fingerprinting benötigen. Genau genommen....

Denn viele Bundler unterstützen Tree Shaking unterhalb der Dateiebene, nämlich auf der Ebene einzelner Definitionen. Dazu müssen sie z. B. Prüfen Sie bei Konstanten, ob sie "rein" sind. Beispielsweise darf die folgende Definition auch dann nicht gelöscht werden, wenn auf x nicht zugegriffen wird:

Eine Definition von const x = 3 hingegen ist sauber und kann nach Belieben entsorgt werden.

Es gibt andere Komprimierungstechniken, wie zB: B. Umbenennen von langen Klassen-, Funktions- und Variablennamen. Natürlich gibt es in einer so dynamischen Sprache wie JavaScript, wo auch der Feldzugriff über Strings möglich ist, eine Extraportion Fallstricke. Dementsprechend sollten Tests nach Möglichkeit auch die verarbeiteten Artefakte überprüfen.

Wurde die Webanwendung mit Hilfe eines Bundlers leistungsmäßig stark abgesenkt, hat das Ergebnis, das vom Browser ausgeführt und angezeigt wird, oft nur noch bedingt Ähnlichkeit mit dem Original. Insbesondere der letzte oben beschriebene Schritt untergräbt die Lesbarkeit.

Aber auch dagegen gibt es ein Rezept. Im Debug-Modus können Bundler so konfiguriert werden, dass sie eine „Source Map“ ausgeben. Der - mehr oder weniger unveränderte - Quelltext wird als Kommentar im Header der Zusammenstellung abgelegt. Das nimmt natürlich alle Größeneinsparungen ad absurdum, also sollte man das nicht in der Produktion einschalten. Aber während der Entwicklungsphase können Browser die Quellzuordnung verwenden, um z. B. genaue Stack-Traces zu rekonstruieren, was die Fehlersuche erheblich vereinfacht.

An spezifischen Benchmarks führt jedoch kein Weg vorbei. Zum einen können Sie den mit allen modernen Browsern mitgelieferten Web Inspector verwenden, der Netzwerkanfragen mit und ohne Cache anzeigen kann. Dadurch können Engpässe schnell identifiziert werden. Ein weiterer Baustein sind automatische Tools, die auch andere Probleme aufdecken können – etwa eine fehlerhafte Darstellung in mobilen Geräten.

Schließlich rate ich Ihnen immer wieder gerne, die neu entwickelte Website auf Ihrem Smartphone über das mobile Internet im Zug aufzurufen. Denn: Wenn es dann rund läuft, läuft es überall.

Ein moderner Bundler muss viel leisten. Die wichtigsten Operationen haben wir in diesem Artikel kennengelernt:

... und alles so effizient wie möglich, so dass man als Entwickler nur Strg S drücken muss und dann alles im Hintergrund passiert. Weitere Aspekte, auf die wir hier nicht eingehen konnten, sind: B. die unterschiedlichen Modulformate in JavaScript, Integration unterschiedlicher Toolchains, universelles JavaScript in Node und im Browser, Testing und so weiter. Hierzu sollten Sie dann die Dokumentation des im Projekt verwendeten Bundlers konsultieren.